|
|
<script lang="ts">
import { beforeNavigate } from '$app/navigation';
import { updateComics } from '$gql/Mutations';
import { comicQuery } from '$gql/Queries';
import { omitIdentifiers, type OmitIdentifiers } from '$gql/Utils';
import {
UpdateMode,
type FullComicFragment,
type PageFragment,
type UpdateComicInput
} from '$gql/graphql';
import { comicPending } from '$lib/Form';
import { toastFinally } from '$lib/Toasts';
import { preventOnPending } from '$lib/Utils';
import BookmarkButton from '$lib/components/BookmarkButton.svelte';
import Guard from '$lib/components/Guard.svelte';
import Head from '$lib/components/Head.svelte';
import OrganizedButton from '$lib/components/OrganizedButton.svelte';
import RemovePageButton from '$lib/components/RemovePageButton.svelte';
import SubmitButton from '$lib/components/SubmitButton.svelte';
import Titlebar from '$lib/components/Titlebar.svelte';
import Grid from '$lib/containers/Grid.svelte';
import ComicForm from '$lib/forms/ComicForm.svelte';
import Gallery from '$lib/gallery/Gallery.svelte';
import PageView from '$lib/reader/PageView.svelte';
import Reader, { initReaderContext } from '$lib/reader/Reader.svelte';
import ComicScrapeForm from '$lib/scraper/ComicScrapeForm.svelte';
import { initScraperContext } from '$lib/scraper/Scraper.svelte';
import { initSelectionContext } from '$lib/selection/Selection.svelte';
import ComicDelete from '$lib/tabs/ComicDelete.svelte';
import ComicDetails from '$lib/tabs/ComicDetails.svelte';
import Tab from '$lib/tabs/Tab.svelte';
import Tabs from '$lib/tabs/Tabs.svelte';
import SelectionControls from '$lib/toolbar/SelectionControls.svelte';
import { getContextClient } from '@urql/svelte';
import { untrack } from 'svelte';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
const client = getContextClient();
const reader = initReaderContext();
const scraper = initScraperContext();
const selection = initSelectionContext<PageFragment>('Page', (p) => p.path);
let comic: FullComicFragment | undefined = $state();
let input: OmitIdentifiers<FullComicFragment> | undefined = $state();
let updateInput = $state(true);
function invalidateInput() {
updateInput = true;
}
function submit(input: UpdateComicInput) {
updateComics(client, { ids: data.id, input }).then(invalidateInput).catch(toastFinally);
}
function toggle(field: keyof Pick<UpdateComicInput, 'bookmarked' | 'favourite' | 'organized'>) {
if (!comic) return;
updateComics(client, { ids: data.id, input: { [field]: !comic[field] } }).catch(toastFinally);
}
function updateCover(id: number) {
updateComics(client, { ids: data.id, input: { cover: { id } } }).catch(toastFinally);
}
function removePages() {
updateComics(client, {
ids: data.id,
input: { pages: { ids: selection.ids, options: { mode: UpdateMode.Remove } } }
})
.then(selection.clear)
.catch(toastFinally);
}
beforeNavigate((navigation) => preventOnPending(navigation, pending));
let result = $derived(comicQuery(client, { id: data.id }));
let pending = $derived(comicPending(comic, input));
$effect(() => {
if (!$result.stale) {
untrack(() => {
if ($result.data?.comic.__typename === 'FullComic') {
comic = $result.data.comic;
if (updateInput) {
input = omitIdentifiers($result.data.comic);
updateInput = false;
}
reader.pages = comic.pages;
selection.view = comic.pages;
scraper.reset();
}
});
}
});
</script>
<Head section="Comic" title={comic?.title} />
{#if comic && input}
<Grid>
<header>
<Titlebar
title={comic.title}
subtitle={comic.originalTitle}
favourite={comic.favourite}
onfavourite={() => toggle('favourite')}
/>
</header>
<aside>
<Tabs badges={{ edit: pending }}>
<Tab initial id="details" title="Details">
<ComicDetails {comic} />
</Tab>
<Tab id="edit" title="Edit">
<div class="flex flex-col gap-4">
<div class="flex gap-2 text-sm">
<SelectionControls page>
<RemovePageButton onclick={removePages} />
</SelectionControls>
<div class="grow"></div>
<BookmarkButton bookmarked={comic.bookmarked} onclick={() => toggle('bookmarked')} />
<OrganizedButton organized={comic.organized} onclick={() => toggle('organized')} />
</div>
<ComicForm bind:input {submit}>
<div class="flex gap-2">
<div class="grow"></div>
<SubmitButton {pending} />
</div>
</ComicForm>
</div>
</Tab>
<Tab id="scrape" title="Scrape">
<ComicScrapeForm {comic} onupsert={invalidateInput} />
</Tab>
<Tab id="deletion" title="Delete">
<ComicDelete {comic} />
</Tab>
</Tabs>
</aside>
<main class="overflow-auto">
<Gallery pages={comic.pages} open={reader.open} {updateCover} />
</main>
</Grid>
<Reader>
<PageView layout={input.layout} direction={input.direction} />
{#snippet sidebar()}
{#if input}
<ComicForm bind:input {submit}>
<div class="flex justify-end gap-2">
<SubmitButton {pending} />
</div>
</ComicForm>
{/if}
{/snippet}
</Reader>
{:else}
<Guard {result} />
{/if}
|